#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using XNALibrary.Interfaces;
using XNALibrary.Utils;
using XNALibrary.Entities;
using XNALibrary.PostProcessing;
using XNALibrary.Lighting;
using XNALibrary.Materials;
using XNALibrary.Cameras;
using XNALibrary.Skybox;
using XNALibrary.Materials;
#endregion

namespace XNALibrary.Game
{
    public class BaseGame : Microsoft.Xna.Framework.Game
    {
        public static GraphicsDeviceManager graphics;
        public static GraphicsDevice device;
        public static ContentManager content;
        public static Skybox.Skybox skybox;
        public static ICamera camera;
        public static GameTime gameTime;
        public static List<Entity> scene;
        public static Light[] lights;
        public PostProcessingManager postProcessing;
        public DepthOfField depthOfField;
        private FpsCounter fpsCounter;
        private bool showFps;
        private bool debugLights;
        private Color backgroundColor;

        #region Properties
        public bool DebugLights
        {
            get { return debugLights; }
            set { debugLights = value; }
        }

        public Color BackgroundColor
        {
            get { return backgroundColor; }
            set { backgroundColor = value; }
        }

        public bool ShowFps
        {
            get { return showFps; }
            set { showFps = value; }
        } 

        #endregion       

        #region Constructor & Initialize
        public BaseGame()
        {
            graphics = new GraphicsDeviceManager(this);
            content = new ContentManager(Services);
            showFps = true;
            backgroundColor = Color.CornflowerBlue;
            scene = new List<Entity>();
            lights = new Light[1];
        }

        protected override void Initialize()
        {
            device = graphics.GraphicsDevice;
            fpsCounter = new FpsCounter();

            depthOfField = new DepthOfField();

            //Initialize lights
            lights[0] = new Light(new Vector4(50, 50, 50, 1));
            for (int i = 0; i < lights.Length; i++)
                lights[i].Initialize();

            //Initialize Game
            Init(); 

            //Initialize Skybox
            if (skybox != null)
                skybox.Initialize();

            base.Initialize();
        }

        protected virtual void Init() { }

        #endregion

        #region LoadContent
        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            if (loadAllContent)
            {
                LoadContent();
            }

            LoadManualContent();
        }

        protected virtual void LoadContent() { }

        protected virtual void LoadManualContent() { } 

        #endregion

        #region UnloadContent
        protected override void UnloadGraphicsContent(bool unloadAllContent)
        {
            if (unloadAllContent)
            {
                // TODO: Unload any ResourceManagementMode.Automatic content
                content.Unload();
            }

            // TODO: Unload any ResourceManagementMode.Manual content
        }

        protected virtual void UnloadContent() { }
        protected virtual void UnloadManualContent() { } 

        #endregion

        #region UpdateGame
		protected override void Update(GameTime gameTime)
        {
            BaseGame.gameTime = gameTime;
            if(showFps)
                Window.Title = fpsCounter.Update();
            UpdateGame();
        }

        protected virtual void UpdateGame() { }

	    #endregion

        #region RenderGame
        protected override void Draw(GameTime gameTime)
        {
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, BackgroundColor, 1.0f, 0);

            //DepthOfField
            depthOfField.GetDepthMap();

            lights[0].GetShadowMap();
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, BackgroundColor, 1.0f, 0);

            //RenderSkybox
            if (skybox != null)
                skybox.Draw(camera.Position, camera.WoldViewProj);

            //RenderLights
            if (debugLights)
                for (int i = 0; i < lights.Length; i++)
                    lights[i].Draw();

            //RenderScene
            foreach (Entity e in scene)
                e.Draw();

            //DepthOfField composition
            depthOfField.Resolve();

            //PostProcessing Render
            if (postProcessing != null)
                postProcessing.PostRender();

            base.Draw(gameTime);
        }
        #endregion

        #region InternalClass
        public class DepthOfField
        {
            protected DepthMap depthMap;
            protected RenderTarget2D depthRenderTarget;
            private PostProcessingManager postProcess;
            private bool active;

            #region Properties
            public bool Active
            {
                get { return active; }
                set { active = value; }
            }

            public RenderTarget2D DepthRenderTarget
            {
                get { return depthRenderTarget; }
                set { depthRenderTarget = value; }
            }

            public DepthMap DepthMap
            {
                get { return depthMap; }
                set { depthMap = value; }
            } 

            #endregion

            public DepthOfField()
            {
                active = false;
                depthMap = new DepthMap();

                PresentationParameters pp = graphics.GraphicsDevice.PresentationParameters;
                DepthRenderTarget = new RenderTarget2D(graphics.GraphicsDevice,
                    pp.BackBufferWidth, pp.BackBufferHeight, 1,
                    pp.BackBufferFormat, pp.MultiSampleType, pp.MultiSampleQuality);

                postProcess = new PostProcessingManager();
                postProcess.AddFilter(new GaussianBlurFilter(0, GaussianBlurFilter.Direction.Horizontal));
                postProcess.AddFilter(new GaussianBlurFilter(1, GaussianBlurFilter.Direction.Vertical));
                postProcess.AddFilter(new DofCombineFilter(0,2));
            }

            public void GetDepthMap()
            {
                if (active)
                {
                    device.SetRenderTarget(0, depthRenderTarget);

                    device.Clear(Color.White);
                    foreach (Entity e in scene)
                        e.Draw(depthMap);

                    device.ResolveRenderTarget(0);
                    device.Textures[2] = depthRenderTarget.GetTexture();
                    device.SetRenderTarget(0, null);
                }
            }

            public void Resolve()
            {
                if(active)
                    postProcess.PostRender();
            }

        }

        #endregion
    }
}
